#include "ch32v_eeprom.h"
#include <string.h>
#include "Arduino.h"
#ifdef __cplusplus
extern "C"
{
#endif

#define FLASH_PAGES_TO_BE_PROTECTED FLASH_WRProt_Pages60to63

#if defined(CH32V3xx)
#define PAGE_WRITE_START_ADDR ((uint32_t)0x0803FC00) /* Start from 255K */
#define PAGE_WRITE_END_ADDR ((uint32_t)0x08040000)   /* End at 256K */
#else
#define PAGE_WRITE_START_ADDR ((uint32_t)0x0800FC00) /* Start from 63K */
#define PAGE_WRITE_END_ADDR ((uint32_t)0x08010000)   /* End at 64K */
#endif
    static uint8_t eeprom_buffer[E2END + 1] __attribute__((aligned(2))) = {0};
    volatile FLASH_Status FLASHStatus = FLASH_COMPLETE;

    uint8_t eeprom_read_byte(const uint32_t pos)
    {
        // eeprom_buffer_fill();
        return eeprom_buffered_read_byte(pos);
    }

    void eeprom_write_byte(uint32_t pos, uint8_t value)
    {
        eeprom_buffered_write_byte(pos, value);
        // eeprom_buffer_flush();
    }

    /* Prived functions ------------------------------------------------------- */
    void eeprom_buffer_fill()
    {

        // Serial.println("===================================");
        uint32_t address = PAGE_WRITE_START_ADDR;
        for (size_t i = 0; i <= E2END; i++)
        {
            eeprom_buffer[i] = *(__IO uint8_t *)address;
            address++;
        }

        // uint32_t address = PAGE_WRITE_START_ADDR;
        // uint32_t address_end = PAGE_WRITE_END_ADDR - 1;
        // address = PAGE_WRITE_START_ADDR;
        // Serial.println("Program Cheking...");
        // while (address <= address_end)
        // {
        //     uint16_t d = (*(__IO uint8_t *)address);
        //     Serial.print(d, 16);
        //     address += 1;
        // }

        // if (chk)
        // {
        //     Serial.println("Memory Program PASS!");
        // }
        // else
        // {
        //     Serial.println("Memory Program FAIL!");
        // }

        // for (size_t i = 0; i < E2END; i++)
        // {
        //     Serial.print(eeprom_buffer[i], 16);
        //     Serial.print(" ");
        // }
        // Serial.println();
    }

    void eeprom_buffer_flush()
    {

//    for (size_t i = 0; i < E2END; i++)
//     {
//         Serial.print(eeprom_buffer[i], 16);
//         Serial.print(" ");
//     }
//     Serial.println();
#if defined(CH32V3xx)

        uint32_t pagesize = FLASH_PAGE_SIZE / 256;
        uint32_t address = PAGE_WRITE_START_ADDR;
        uint32_t offset = 0;
        uint8_t *data = 0;

        FLASH_Unlock_Fast();

        // TODO: 待验证. 一页256个字节

        for (int i = 0; i < pagesize; i++)
        {
            address += (i * 256);
            FLASH_ErasePage_Fast(address);

            data = (uint8_t*) eeprom_buffer + offset;
            offset += 256;

            FLASH_ProgramPage_Fast(address, (uint32_t*)data);
        }
        

        FLASH_Lock_Fast();
#else
        uint32_t pagesize = FLASH_PAGE_SIZE / 128;
        uint32_t address = PAGE_WRITE_START_ADDR;
        uint32_t offset = 0;
        uint8_t *data = 0;

        FLASH_Unlock_Fast();

        for (int i = 0; i < pagesize; i++)
        {
            address += (i * 128);
            FLASH_ErasePage_Fast(address);
            FLASH_BufReset();
            for (int j = 0; j < 128; j += 0x10)
            {
                data = ((uint8_t *)eeprom_buffer + offset);
                FLASH_BufLoad(address + j, *(data++), *(data++), *(data++), *(data++));
                offset += 4;
            }

            FLASH_ProgramPage_Fast(address);
        }
        FLASH_Lock_Fast();
 #if 0
    uint32_t offset = 0;
    uint32_t address = PAGE_WRITE_START_ADDR;
    uint32_t address_end = PAGE_WRITE_END_ADDR - 1;
    uint32_t data = 0;

    uint32_t WRPR_Value = 0xFFFFFFFF;
    uint32_t ProtectedPages = 0x0;
    //  Serial.println("++++++++++++++++++++++++++++++++++++++");
    FLASH_Unlock();
    WRPR_Value = FLASH_GetWriteProtectionOptionByte();
    // Serial.printf("WRPR_Value: %d \n", WRPR_Value);
    if ((WRPR_Value & FLASH_PAGES_TO_BE_PROTECTED) != 0x00)
    {
        FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);

        FLASHStatus = FLASH_ErasePage(PAGE_WRITE_START_ADDR);
        // Serial.print("FLASH_ErasePage: ");
        // Serial.println(FLASHStatus);
        // Serial.println("Programing...");
        while ((address <= address_end) && (FLASHStatus == FLASH_COMPLETE))
        {
            data = *((uint32_t *)((uint8_t *)eeprom_buffer + offset));

            FLASHStatus = FLASH_ProgramWord(address, data);
            // FLASHStatus = FLASH_ProgramHalfWord(address, data);
            //  Serial.printf("FLASHStatus: %d address: %X offset: %d  data: %X \n", FLASHStatus, address, offset, data);
            address += 4;
            offset += 4;
        }
    }
    else
    {
        core_debug("Error to program the flash : The desired pages are write protected\r\n");
        // Serial.println("Error to program the flash : The desired pages are write protected\r\n");
    }

    FLASH_Lock();
 #endif
#endif
        // Serial.println("Programing OK");
        // address = PAGE_WRITE_START_ADDR;
        // core_debug("Program Cheking...");
        // bool chk = true;
        // while (address <= PAGE_WRITE_END_ADDR - 1)
        // {
        //     uint16_t d = (*(__IO uint16_t *)address);
    
        //     core_debug("%X ", d);
        //     address += 2;
        // }

        // if (chk)
        // {
        //     Serial.println("Memory Program PASS!");
        // }
        // else
        // {
        //     Serial.println("Memory Program FAIL!");
        // }
    }

    uint8_t eeprom_buffered_read_byte(const uint32_t pos)
    {
        return eeprom_buffer[pos];
    }

    void eeprom_buffered_write_byte(uint32_t pos, uint8_t value)
    {
        eeprom_buffer[pos] = value;
    }

#ifdef __cplusplus
}
#endif